package com.gallsoft.amidar;

import android.content.Context;
import android.widget.Toast;

public class Game extends AllocationGuard {
    private GameThread mGameThread;
    private Thread mGame;
    private ObjectManager mGameRoot;
    
    private GameRenderer mRenderer;
    private boolean mRunning;
    private GLSurfaceView mSurfaceView;
    private boolean mBootstrapComplete;
    private boolean mGLDataLoaded;
    private ContextParameters mContextParameters;

    public Game() {
        super();
        mRunning = false;
        mBootstrapComplete = false;
        mGLDataLoaded = false;
        mContextParameters = new ContextParameters();
    }

    /** 
     * Creates core game objects and constructs the game engine object graph.  Note that the
     * game does not actually begin running after this function is called (see start() below).
     * Also note that textures are not loaded from the resource pack by this function, as OpenGl
     * isn't yet available.
     * @param context
     */
    public void bootstrap(Context context, int viewWidth, int viewHeight, int gameWidth, int gameHeight) {
        if (!mBootstrapComplete) {
            mRenderer = new GameRenderer(context, this, gameWidth, gameHeight);
    
            // Create core systems
            BaseObject.sSystemRegistry.openGLSystem = new OpenGLSystem(null);
            
            ContextParameters params = mContextParameters;
            params.viewWidth = viewWidth;
            params.viewHeight = viewHeight;
            params.gameWidth = gameWidth;
            params.gameHeight = gameHeight;
            params.viewScaleX = (float)viewWidth / gameWidth;
            params.viewScaleY = (float)viewHeight / gameHeight;
            params.context = context;
            BaseObject.sSystemRegistry.contextParameters = params;
    
            // Short-term textures are cleared between levels.
            TextureLibrary shortTermTextureLibrary = new TextureLibrary();
            BaseObject.sSystemRegistry.shortTermTextureLibrary = shortTermTextureLibrary;
            
            // Long-term textures persist between levels.
            TextureLibrary longTermTextureLibrary = new TextureLibrary();
            BaseObject.sSystemRegistry.longTermTextureLibrary = longTermTextureLibrary;
            
            // The buffer library manages hardware VBOs.
            BaseObject.sSystemRegistry.bufferLibrary = new BufferLibrary();
            
            BaseObject.sSystemRegistry.soundSystem = new SoundSystem();
            
            // The root of the game graph.
            MainLoop gameRoot = new MainLoop();
    
            InputSystem input = new InputSystem();
            BaseObject.sSystemRegistry.inputSystem = input;
            BaseObject.sSystemRegistry.registerForReset(input);

            InputGameInterface inputInterface = new InputGameInterface();
            gameRoot.add(inputInterface);
            BaseObject.sSystemRegistry.inputGameInterface = inputInterface;

            LevelSystem level = new LevelSystem();
            BaseObject.sSystemRegistry.levelSystem = level;

            GameObjectManager gameManager = new GameObjectManager(params.viewWidth * 2);
            BaseObject.sSystemRegistry.gameObjectManager = gameManager;
            
            GameObjectFactory objectFactory = new GameObjectFactory();
            BaseObject.sSystemRegistry.gameObjectFactory = objectFactory;
            
            BaseObject.sSystemRegistry.hotSpotSystem = new HotSpotSystem();

            gameRoot.add(gameManager);
            
            RenderSystem renderer = new RenderSystem();
            BaseObject.sSystemRegistry.renderSystem = renderer;
            BaseObject.sSystemRegistry.vectorPool = new VectorPool();
            BaseObject.sSystemRegistry.drawableFactory = new DrawableFactory();

            mGameRoot = gameRoot;
            
            mGameThread = new GameThread(mRenderer);
            mGameThread.setGameRoot(mGameRoot);
    
            
            //mCurrentLevel = null;
            
            mBootstrapComplete = true;
        }
    }
    
    public boolean onKeyDownEvent(int keyCode) {
        boolean result = false;
        if (mRunning) {
            BaseObject.sSystemRegistry.inputSystem.keyDown(keyCode);
        }
        return result;
    }
    
    public boolean onKeyUpEvent(int keyCode) {
        boolean result = false;
        if (mRunning) {
        	BaseObject.sSystemRegistry.inputSystem.keyUp(keyCode);
        }
        return result;
    }

    public GameRenderer getRenderer() {
        return mRenderer;
    }  
    
    public void onPause() {
    	if (mRunning) {
    		mGameThread.pauseGame(); 
    	}
    }
    
    public void onResume(Context context, boolean force) {
    	if (force && mRunning) {
    		mGameThread.resumeGame();
    	} else {
	        mRenderer.setContext(context);
	        // Don't explicitly resume the game here.  We'll do that in
	        // the SurfaceReady() callback, which will prevent the game
	        // starting before the render thread is ready to go.
	        BaseObject.sSystemRegistry.contextParameters.context = context;
    	}
    }
    
    public void onSurfaceReady() {
        DebugLog.d("Pooyan", "Surface Ready");

        if (!mRunning) {
        	goToLevel();
        }
        else if (mGameThread.getPaused() && mRunning) {
            mGameThread.resumeGame();
        } 
    }

    protected synchronized void goToLevel() {
        
        ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
        BaseObject.sSystemRegistry.levelSystem.loadLevel(mGameRoot);
        
        Context context = params.context;
        mRenderer.setContext(context);
        mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary);
        mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
        mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary);
        
        mGLDataLoaded = true;
        
        
        TimeSystem time = BaseObject.sSystemRegistry.timeSystem;
        time.reset();
        
        /*
        HudSystem hud = BaseObject.sSystemRegistry.hudSystem;
        if (hud != null) {
            hud.startFade(true, 1.0f);
        }
        
        CustomToastSystem toast = BaseObject.sSystemRegistry.customToastSystem;
        if (toast != null) {
        	if (level.inThePast) {
        		toast.toast(context.getString(R.string.memory_playback_start), Toast.LENGTH_LONG);
        	} else {
        		if (mLastLevel != null && mLastLevel.inThePast) {
            		toast.toast(context.getString(R.string.memory_playback_complete), Toast.LENGTH_LONG);
        		}
        	}
        }
        
        mLastLevel = level;
        */
        start();
    }
    

    public void setSurfaceView(GLSurfaceView view) {
        mSurfaceView = view;
    }
    
    public void onSurfaceLost() {
        DebugLog.d("Pooyan", "Surface Lost");
        
        BaseObject.sSystemRegistry.shortTermTextureLibrary.invalidateAll();
        BaseObject.sSystemRegistry.longTermTextureLibrary.invalidateAll();
        BaseObject.sSystemRegistry.bufferLibrary.invalidateHardwareBuffers();

        mGLDataLoaded = false;
    }

    public void onSurfaceCreated() {
        DebugLog.d("Pooyan", "Surface Created");
        
        // TODO: this is dumb.  SurfaceView doesn't need to control everything here.
        // GL should just be passed to this function and then set up directly.
        
        if (!mGLDataLoaded && mGameThread.getPaused() && mRunning) {
        	
            mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary);
            mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
            mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary);
            mGLDataLoaded = true;
        }  
    }

    /** Starts the game running. */
    public void start() {
        if (!mRunning) {
            assert mGame == null;
            
            // Now's a good time to run the GC.
            Runtime r = Runtime.getRuntime();
            r.gc();
            
            DebugLog.d("Pooyan", "Start!");
            
            mGame = new Thread(mGameThread);
            mGame.setName("Game");
            mGame.start();
            mRunning = true;            
            AllocationGuard.sGuardActive = false;
        } 
        else {
            mGameThread.resumeGame();
        }
    }

    public void stop() {
        if (mRunning) {
            DebugLog.d("Pooyan", "Stop!");
            
            if (mGameThread.getPaused()) {
                mGameThread.resumeGame();
            }
            
            mGameThread.stopGame();
            
            try {
                mGame.join();
            } 
            catch (InterruptedException e) {
                mGame.interrupt();
            }
            
            mGame = null;
            mRunning = false;
            AllocationGuard.sGuardActive = false;
        }
    }

}
